package com.mattgarner.jaddas.common;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.ArrayList;

public class ResultSetHeader {
	private ArrayList<String> columnNames;
	private ArrayList<Byte> columnTypes;
	
	private byte[] headerBytes;
	private boolean headerBytesValid = false;
	
	private String strEncoding;
	private int cursor = 0;
	
	
	public ResultSetHeader(String stringEncoding) {
		strEncoding = stringEncoding;
		columnNames = new ArrayList<String>();
		columnTypes = new ArrayList<Byte>();
		headerBytesValid = false;
	}
	
	public ResultSetHeader(String stringEncoding, byte[] hdrBytes) throws ResultSetException {
		strEncoding = stringEncoding;
		headerBytes = hdrBytes;
		headerBytesValid = true;
		parseHeaderBytes();
	}
	
	
	public final void setColumn(int index, byte columnType, String columnName) throws ResultSetException {
		columnTypes.add(index, columnType);
		columnNames.add(index, columnName);
		if(index > cursor) { cursor = index + 1; }
		headerBytesValid = false;
	}
	
	public final void addColumn(byte columnType, String columnName) throws ResultSetException {
		setColumn(cursor, columnType, columnName);
		cursor++;
	}
	
	public final void addIntColumn(String columnName) throws ResultSetException {
		addColumn(ColumnType.COLUMN_TYPE_INT, columnName);
	}
	
	public final void addShortColumn(String columnName) throws ResultSetException {
		addColumn(ColumnType.COLUMN_TYPE_SHORT, columnName);
	}
	
	public final void addLongColumn(String columnName) throws ResultSetException {
		addColumn(ColumnType.COLUMN_TYPE_LONG, columnName);
	}
	
	public final void addStringColumn(String columnName) throws ResultSetException {
		addColumn(ColumnType.COLUMN_TYPE_VARCHAR, columnName);
	}

	
	public final byte[] getHeaderDataStream()  {
		if(headerBytesValid) {  // if headerBytes is still valid, don't recreate them
			return headerBytes;
		}
		
		ByteArrayOutputStream baosHeader = new ByteArrayOutputStream();
		DataOutputStream header = new DataOutputStream(baosHeader);
		try { 
			header.writeByte((byte)columnTypes.size());
			for(int a=0; a<columnTypes.size(); a++) {
				
				header.writeByte(a);
				header.writeByte(columnTypes.get(a));
				byte[] encodedString = columnNames.get(a).getBytes(strEncoding);
				header.writeInt(encodedString.length);
				header.write(encodedString);	
			}
			header.close();
			baosHeader.close();
			headerBytes = baosHeader.toByteArray();
			headerBytesValid = true;
			return headerBytes;
		} catch(Exception e) {
			// shouldn't ever happen ...
			//throw new ResultSetException("Error Encoding Header Byte Format.");
			return null;
		}
	}
	
	public final void parseHeaderBytes() throws ResultSetException {
		ByteArrayInputStream baisHeader = new ByteArrayInputStream(headerBytes);
		DataInputStream header = new DataInputStream(baisHeader);
		
		try {
			int headerSize = (int)header.readByte();
			columnNames = new ArrayList<String>(headerSize);
			columnTypes = new ArrayList<Byte>(headerSize);
			
			for(int a = 0; a<headerSize; a++) {
				byte colIndex = header.readByte();
				if((int)colIndex != a) {
					throw new ResultSetException("Column header out-of-order.");
				}
				columnTypes.add(a, header.readByte());
				int encodedStringLength = header.readInt();
				byte[] encodedString = new byte[encodedStringLength];
				header.read(encodedString, 0, encodedStringLength);
				columnNames.add(a, new String(encodedString, strEncoding));
			}
			header.close();
			baisHeader.close();
			headerBytesValid = true;
		} catch(Exception e) {
		  throw new ResultSetException("Error Decoding Header Byte Format: " + e.getMessage() );
		}		
	}
	
	public final int getColumnCount() {
		return columnTypes.size();
	}
	
	public final String getStringEncoding() {
		return strEncoding;
	}
	
	public final byte getColumnType(int index) throws ResultSetException {
		try { 
			return columnTypes.get(index);
		} catch(IndexOutOfBoundsException e) {
			throw new ResultSetException("getColumnType: index out-of-bounds");
		}
	}
	
	public final String getColumnTypesString() {
		return columnTypes.toString();
	}
	
	
	public final String getColumnName(int index) {
		return columnNames.get(index);
	}
}
